package Noyau;

import java.util.ArrayList;
import java.util.List;

import Outils.Dijkstra;
import Outils.DijkstraResult;

public class CheminLien extends Chemin implements InterfaceChariot, InterfaceBagage
{
	private boolean sens;
	private List<InterfaceChariot> entreesChariots = new ArrayList<InterfaceChariot>();
	private List<InterfaceChariot> sortiesChariots = new ArrayList<InterfaceChariot>();
	private InterfaceBagage entreeBagage = null;
	private InterfaceBagage sortieBagage = null;
	private Chariot chariot = null;
	private Bagage bagage = null;
	private long bagageCount = 0;

	public boolean isSens ()  
	{
		return this.sens;
	}

	public void setIsSens(boolean value)
	{
		this.sens = value;

		this.setChanged();
		this.notifyObservers();
	}

	public Chariot getChariot()
	{
		return this.chariot;
	}

	@Override
	public List<InterfaceChariot> getEntrees() 
	{
		return this.entreesChariots;
	}

	@Override
	public List<InterfaceChariot> getSorties() 
	{
		return this.sortiesChariots;
	}

	@Override
	public void addEntree(InterfaceChariot value)
	{
		if (this.entreesChariots.contains(value))
		{
			return;
		}

		this.entreesChariots.add(value);

		this.setChanged();
		this.notifyObservers();
	}

	@Override
	public void addSortie(InterfaceChariot value)
	{
		if (this.sortiesChariots.contains(value))
		{
			return;
		}

		this.sortiesChariots.add(value);
		value.addEntree(this);		

		this.setChanged();
		this.notifyObservers();
	}

	@Override
	public InterfaceBagage getEntree() 
	{
		return this.entreeBagage;
	}

	@Override
	public InterfaceBagage getSortie() 
	{
		return this.sortieBagage;
	}

	@Override
	public void setEntree(InterfaceBagage value)
	{
		this.entreeBagage = value;

		this.setChanged();
		this.notifyObservers();
	}

	@Override
	public void setSortie(InterfaceBagage value)
	{
		this.sortieBagage = value;
		value.setEntree(this);

		this.setChanged();
		this.notifyObservers();
	}
	
	public long getBagageCount()
	{
		return this.bagageCount;
	}

	@Override
	public void CreerChariot(Chariot chariot) {
		chariot.setParent(this);
		this.chariot = chariot;

		this.setChanged();
		this.notifyObservers();
	}

	@Override
	public boolean PeutAjouterChariot(Chariot newChariot)
	{
		// If we already have a chariot, we return false.
				if (this.chariot != null)
				{
					return false;
				}

				return true;
	}

	@Override
	public boolean AjouterChariot(Chariot newChariot) 
	{
		if (!this.PeutAjouterChariot(newChariot))
		{
			return false;
		}

		// Otherwise, we take the chariot and return true.
		this.CreerChariot(newChariot);

		return true;
	}

	@Override
	public void CreerBagage(Bagage bagage) 
	{
		bagage.setParent(this);
		this.bagage = bagage;

		this.setChanged();
		this.notifyObservers();
	}

	@Override
	public boolean PeutAjouterBagage(Bagage newBagage)
	{
		// If we already have a bagage, we return false.
		if (this.bagage != null)
		{
			return false;
		}

		return true;
	}

	@Override
	public boolean AjouterBagage(Bagage newBagage) 
	{
		if (!this.PeutAjouterBagage(newBagage))
		{
			return false;
		}

		// Otherwise, we take the bagage and return true.
		this.CreerBagage(newBagage);

		return true;
	}

	@Override
	public void TickHorloge() 
	{
		if (this.sens)
		{
			// If we have a chariot, we try to give it to the next element.
			if (this.chariot != null)
			{
				// If we a have a bagage, and an empty chariot, we load the chariot.
				if (this.chariot.getBagage() == null && this.bagage != null)
				{
					// We load the bagage onto the chariot.
					this.chariot.setBagage(this.bagage);
					this.bagage = null;
					this.bagageCount++;
				}

				if (this.chariot.getBagage() != null)
				{
					// We try to give the chariot to the next chariot element.
					this.giveChariotToNext();					
				}
			}
			else if (this.bagage != null)
			{

			}
		}
		else
		{
			// If we have a chariot, we first try to unload it, and if possible we give it to the next element.
			if (this.chariot != null)
			{
				if (this.chariot.getBagage() != null)
				{
					if (this.bagage == null)
					{
						// We check if this bagage must be unloaded here.
						DijkstraResult dijResult = Dijkstra.computeShortestPaths(this);
						Chemin nextElement = Dijkstra.getNextElement(dijResult.getPrevious(), this, this.chariot.getBagage().getArrivee());

						if (this.sortieBagage == nextElement)
						{						
							// We unload the bagage from the chariot.		
							if (this.AjouterBagage(this.chariot.getBagage()))
							{
								this.chariot.setBagage(null);
							}
						}

						// We try to give the chariot to the next chariot element.
						this.giveChariotToNext();
					}
				}
				else
				{					
					// We try to give the chariot to the next chariot element.
					this.giveChariotToNext();
				}
			}

			// We try to give the bagage to the next element.
			this.giveBagageToNext();
		}

		this.setChanged();
		this.notifyObservers();
	}

	private boolean giveChariotToNext()
	{
		// We first make sure that we have a chariot.
		if (this.chariot == null)
		{
			return false;
		}

		// If we have no sorties, we return.
		if (this.sortiesChariots.size() == 0)
		{
			return false;
		}

		// We try to give the chariot to the first sortie.
		InterfaceChariot firstSortie = this.sortiesChariots.get(0);
		if (!firstSortie.AjouterChariot(this.chariot))
		{
			return false;
		}

		this.chariot = null;

		return true;
	}

	private boolean giveBagageToNext()
	{
		// We first make sure that we have a bagage.
		if (this.bagage == null)
		{
			return false;
		}

		// If we have no sortie we return.
		if (this.sortieBagage == null)
		{
			return false;
		}

		// We try to give the bagage to the next element.
		if (!this.sortieBagage.AjouterBagage(this.bagage))
		{
			return false;
		}

		this.bagage = null;

		return true;
	}

	@Override
	public List<Chemin> getNeighbors()
	{
		List<Chemin> result = new ArrayList<Chemin>();

		for (InterfaceChariot sortie : this.sortiesChariots)
		{
			result.add((Chemin)sortie);
		}		

		if (this.sortieBagage != null)
		{
			result.add((Chemin)this.sortieBagage);			
		}

		return result;
	}

	@Override
	public double getSize()
	{		
//		if (this.sens && this.chariot != null && this.chariot.getBagage() == null && this.bagage == null)
//		{
//			return Double.MAX_VALUE / 2;
//		}
//		else if (this.sens)
//		{
//			return 1000 * (this.bagageCount % 10);
//		}

		return 0;
	}

	@Override
	public double getVisualSize()
	{
		return 0;
	}

	@Override
	public double getAngle()
	{
		double result = 0;

		if (this.entreesChariots.size() > 0 && this.sortiesChariots.size() > 0)
		{
			double angle1 = this.entreesChariots.get(0).getAngle();
			double angle2 = this.sortiesChariots.get(0).getAngle();

			result = (angle1 + angle2) / 2;

			if (Math.abs(angle1 - angle2) > Math.PI)
			{
				result -= Math.PI;
			}
		}

		return result;
	}

	@Override
	public int getZBuffer()
	{
		return 10;
	}

	@Override
	public List<ObjetMetier> getChildren()
	{
		List<ObjetMetier> children = new ArrayList<ObjetMetier>();

		if (this.chariot != null)
		{
			children.add(this.chariot);
		}

		if (this.bagage != null)
		{
			children.add(this.bagage);
		}

		return children;
	}

	@Override
	public void SupprimerChariot(Chariot chariot) {
		if (this.chariot != chariot)
		{
			return;
		}

		this.chariot = null;
		this.setChanged();
	}

	@Override
	public List<Bagage> getBagages() {
		List<Bagage> result = new ArrayList<Bagage>();
		
		if (this.bagage != null)
		{
			result.add(this.bagage);
		}
		
		return result;
	}
}
